package com.atguigu.gmall.cart.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.cart.entity.CartInfo;
import com.atguigu.gmall.cart.service.CartService;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.execption.GmallException;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.UserAuthUtil;
import com.atguigu.gmall.feign.product.ProductSkuDetailFeignClient;
import com.atguigu.gmall.product.entity.SkuInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class CartServiceImpl implements CartService {

    @Autowired
    StringRedisTemplate redisTemplate;
    @Autowired
    ProductSkuDetailFeignClient productSkuDetailFeignClient;
    @Autowired
    ThreadPoolExecutor executor;

    //选用哪个购物车 ==> cartKey
    @Override
    public String determinCartKey() {
        //获取老请求
        HttpServletRequest request = UserAuthUtil.getRequest();
        //判断用户是否登录
        //登录
        String userId = request.getHeader(RedisConst.USER_ID_HEADER);
        if (!StringUtils.isEmpty(userId)) {
            return RedisConst.CART_INFO_KEY + userId;// cartKey = cart:info:用户标识
        }
        //没登陆
        String tempId = request.getHeader(RedisConst.TEMP_ID_HEADER);
        return RedisConst.CART_INFO_KEY + tempId;// cartKey = cart:info:用户标识
    }

    //添加到购物车
    @Override
    public SkuInfo addToCart(Long skuId, Integer skuNum, String cartKey) {
        //1 从购物车中 获取到商品
        CartInfo cartInfo = getItem(skuId, cartKey);

        //2 购物车 没有商品：添加
        if (cartInfo ==null){
            //通过远程调用，获取商品信息
            CartInfo cartInfoToSave = prepareCartInfo(skuId,skuNum);
            //保存到购物车 redis
            saveItem(cartKey, cartInfoToSave);
            //给前端返回的数据
            SkuInfo skuInfo = convertItemToSkuInfo(cartInfoToSave);
            return skuInfo;
        } else {
            //3 购物车 有商品：修改数量
            cartInfo.setSkuNum(cartInfo.getSkuNum()+skuNum);
            cartInfo.setUpdateTime(new Date());
            BigDecimal price = productSkuDetailFeignClient.getPrice(skuId).getData();
            cartInfo.setSkuPrice(price);//商品的实时价格
            //保存到 购物车
            saveItem(cartKey, cartInfo);
            //给前端返回的数据
            SkuInfo skuInfo = convertItemToSkuInfo(cartInfo);
            return skuInfo;
        }
    }

    //从购物车中 获取商品信息
    public CartInfo getItem (Long skuId, String cartKey){
        //从购物车中获取商品
        Object item = redisTemplate.opsForHash().get(cartKey, skuId.toString());
        //没有商品
        if (item == null){
            return null;
        }
        //有商品 json ==> cartInfo
        String json = item.toString();
        CartInfo cartInfo = JSON.parseObject(json, CartInfo.class);
        return cartInfo;
    }

    //准备商品信息 远程调用feign，获取skuinfo详情 sku-info ==> cartInfoToSave
    public CartInfo prepareCartInfo (Long skuId, Integer skuNum){
        //远程调用获取 sku-info
        SkuInfo skuInfo = productSkuDetailFeignClient.getSkuInfo(skuId).getData();

        CartInfo cartInfoToSave = new CartInfo();
        cartInfoToSave.setSkuId(skuInfo.getId());
        cartInfoToSave.setCartPrice(skuInfo.getPrice());//放入购物车时价格
        cartInfoToSave.setSkuPrice(skuInfo.getPrice());//商品的实时价格
        cartInfoToSave.setSkuNum(skuNum);
        cartInfoToSave.setImgUrl(skuInfo.getSkuDefaultImg());
        cartInfoToSave.setSkuName(skuInfo.getSkuName());
        cartInfoToSave.setIsChecked(1);//默认被选中
        cartInfoToSave.setCreateTime(new Date());
        cartInfoToSave.setUpdateTime(new Date());

        return cartInfoToSave;
    }

    //保存商品信息到 购物车
    public void saveItem(String cartKey, CartInfo cartInfo){

        // 购物车单个商品不超过200
        if (cartInfo.getSkuNum() >= RedisConst.CART_ITEM_NUM_LIMIT){
            throw new GmallException(ResultCodeEnum.CART_ITEM_NUM_OVERFLOW);
        }
        //todo 方法1 购物车总商品数量不超过200 判断此操作是新增还是修改
        Long size = redisTemplate.opsForHash().size(cartKey);//购物车的商品 总数量
        Boolean hasKey = redisTemplate.opsForHash().hasKey(cartKey, cartInfo.getSkuId().toString());//看数据库是否有这个商品，有了就是修改数量，如果没有，就是新增，
        if (!hasKey){//新增商品
            if (size +1 >= 200){
                throw new GmallException(ResultCodeEnum.CART_ITEM_COUNT_OVERFLOW);
            }
        }
        redisTemplate.opsForHash().put(cartKey, cartInfo.getSkuId().toString(), JSON.toJSONString(cartInfo));
        //todo 方法2 不管是商品数量新增还是修改，都是先往数据库放，然后得到数量，大于限制，就删除刚刚的操作
/*        redisTemplate.opsForHash().put(cartKey, cartInfo.getSkuId().toString(), JSON.toJSONString(cartInfo));
        Long size = redisTemplate.opsForHash().size(cartKey);
        if (size>200){
            redisTemplate.opsForHash().delete(cartKey, cartInfo.getSkuId().toString());
            throw new GmallException(ResultCodeEnum.CART_ITEM_COUNT_OVERFLOW);
        }*/

    }

    //返回到前端的商品信息
    private SkuInfo convertItemToSkuInfo (CartInfo cartInfoToSave){
        SkuInfo skuInfo = new SkuInfo();
        skuInfo.setId(cartInfoToSave.getSkuId());
        skuInfo.setSkuDefaultImg(cartInfoToSave.getImgUrl());
        skuInfo.setSkuName(cartInfoToSave.getSkuName());
        return skuInfo;
    }

    @Override
    public List<CartInfo> displayItems() {
        //合并购物车
        //判断用户是否登录
        //cartKey = cart:info:用户标识
        String tempCartKey = getCartKey(RedisConst.TEMP_ID_HEADER);
        String userCartKey = getCartKey(RedisConst.USER_ID_HEADER);
        //没登录 就是用临时购物车
        if (userCartKey == null){
            //临时购物车 设置过期时间
            Long expire = redisTemplate.getExpire(tempCartKey);
            if (expire == null){
                redisTemplate.expire(tempCartKey, 365, TimeUnit.DAYS);
            }
            List<CartInfo> cartList = getCartItems(tempCartKey);
            return cartList;
        }
        //登录了 看是否需要合并购物车
        try {
            Long size = redisTemplate.opsForHash().size(tempCartKey);
            if (size>0){
                //临时购物车中商品才进行合并
                List<CartInfo> tempCartList = getCartItems(tempCartKey);
                for (CartInfo cartInfo : tempCartList) {
                    //合并购物车
                    addToCart(cartInfo.getSkuId(), cartInfo.getSkuNum(), userCartKey);
                    //删除临时购物车
                    redisTemplate.delete(tempCartKey);
                }
            }
        } catch (Exception e){
            //说明合并购物车出错，为了能正常显示页面，吃掉异常，
        }

        List<CartInfo> cartInfos = getCartItems(userCartKey);
        return cartInfos;
    }
    //获取购物车标识 cartKey = cart:info:用户标识
    public String getCartKey (String flag){
        HttpServletRequest request = UserAuthUtil.getRequest();
        //获取用户标识
        String tempIdOrUserId = request.getHeader(flag);
        if (StringUtils.isEmpty(tempIdOrUserId)){
            return null;
        }
        String cartKey = RedisConst.CART_INFO_KEY + tempIdOrUserId;
        return cartKey;
    }

    //获取购物车列表
    @Override
    public List<CartInfo> getCartItems(String cartKey) {
        List<CartInfo> cartInfos = redisTemplate.opsForHash()
                .values(cartKey)
                .stream()
                .map((item)->JSON.parseObject(item.toString(), CartInfo.class))//从数据库里得到的是json字符串，转为CartInfo对象
                .sorted((o1,o2)-> o2.getCreateTime().compareTo(o1.getCreateTime()))//根据创建时间排序,o2跟o1进行比较
                .collect(Collectors.toList());
        syncPrice(cartKey, cartInfos);

        //todo: 此处有bug，如果删除购物车中选中的商品，会先调用 getCartItems方法，触发后台同步价格
        // 导致删除的东西会再重新保存到redis，删除失败，解决：使用 putIfPresent
        //1引入线程池
        CompletableFuture.runAsync(()-> { //2异步操作
            syncPrice(cartKey, cartInfos);
        },executor);

        return cartInfos;
    }
    //同步价格
    private void syncPrice(String cartKey, List<CartInfo> cartInfos) {
        //节流
        Long increment = redisTemplate.opsForValue().increment("price:" + cartKey);
        if (increment%10 == 0){}
        //todo 同步最新价格
        cartInfos.stream()
                .forEach((item)->{//遍历每个商品
                    //远程调用
                    BigDecimal realPrice = productSkuDetailFeignClient.getPrice(item.getSkuId()).getData();//商品的真实价格
                    //购物车的价格和实时价格相减取绝对值
                    if (Math.abs(item.getCartPrice().doubleValue() - realPrice.doubleValue()) > 0.0001) {
                        log.info("购物车：{}，商品：{}，价格变为：{}",cartKey,item.getSkuId(),realPrice);
                        //说明价格不相等，重新填充价格
                        item.setCartPrice(realPrice);
                        //保存
                        saveItem(cartKey, item);
                    }
                });
    }

    //修改购物车数量
    @Override
    public void updateItemNum(Long skuId, Integer skuNum, String cartKey) {
        //先获取要修改的商品
        CartInfo cartInfo = getItem(skuId, cartKey);
        //修改商品数量
        //用户可能每次增减一件商品，也可能增减多件商品，这时候就需要考虑购物车商品数量上限
        if ((skuNum == 1 || skuNum == -1)) {
            cartInfo.setSkuNum(cartInfo.getSkuNum() + skuNum);
        }else {
            cartInfo.setSkuNum(skuNum);//假如用户修改数量为 1，需要前端配合，做一个校验，区分请求，
        }
        //保存到redis
        saveItem(cartKey, cartInfo);
    }
    //选中商品 1 ，0
    @Override
    public void checkItem(Long skuId, Integer checked, String cartKey) {
        if (!(checked == 1 || checked == 0)){
            //前端发起攻击请求 抛异常
            throw new GmallException(ResultCodeEnum.INVAILD_PARAM);
        }
        //获取商品
        CartInfo cartInfo = getItem(skuId, cartKey);
        //修改状态
        cartInfo.setIsChecked(checked);
        //保存redis
        saveItem(cartKey, cartInfo);
    }
    //删除商品
    @Override
    public void deleteCart(String cartKey, Long skuId) {
        redisTemplate.opsForHash().delete(cartKey, skuId.toString());
    }

    //删除选中的商品
    @Override
    public void deleteChecked(String cartKey) {
/*        //获取购物车的所有商品
        List<CartInfo> cartInfoList = getCartItems(cartKey);
        //进行流操作
        List<String> list = cartInfoList.stream()
                .filter(o1 -> o1.getIsChecked() == 1)//过滤所有被选中的
                .map(item -> item.getSkuId().toString())//获取商品skuid，转为字符串
                .collect(Collectors.toList());*/

        List<CartInfo> cartInfoList = getCheckeds(cartKey);
        //获取商品skuid，转为字符串
        List<String> list = cartInfoList.stream()
                .map((item) -> item.getSkuId().toString())
                .collect(Collectors.toList());
        redisTemplate.opsForHash().delete(cartKey, list.toArray());//集合转为数组
    }

    //获取所有选中的商品
    @Override
    public List<CartInfo> getCheckeds(String cartKey) {
        //获取购物车的所有商品 todo :直接调用这个方法会有bug
//        List<CartInfo> cartInfoList = getCartItems(cartKey);

        List<CartInfo> cartInfoList = redisTemplate.opsForHash()
                .values(cartKey)
                .stream()
                .map((item)->JSON.parseObject(item.toString(), CartInfo.class))//从数据库里得到的是json字符串，转为CartInfo对象
                .sorted((o1,o2)-> o2.getCreateTime().compareTo(o1.getCreateTime()))//根据创建时间排序,o2跟o1进行比较
                .filter(o1 -> o1.getIsChecked() == 1)//过滤所有被选中的
                .collect(Collectors.toList());

        //进行流操作
//        List<String> list = cartInfoList.stream()
//                .filter(o1 -> o1.getIsChecked() == 1)//过滤所有被选中的
//                .map(item -> item.getSkuId().toString())//获取商品skuid，转为字符串
//                .collect(Collectors.toList());
        return cartInfoList;
    }
}
